#pragma once
#include <ManagerBase.h>
#include <BasisObj.h>

namespace grflib
{
	namespace engine
	{
		namespace core
		{
			/////////////////////////////////////////////////
			//Responsor to GUI and keyboard events
			struct IGUIFunc : public IMemberCaller<IInteractive>
			{
				IGUIFunc(UINT nEvent, int nControlId, CDXUTControl *pControl) :
					m_nEvent(nEvent), m_nControlId(nControlId), m_pControl(pControl) {}
				virtual HRESULT operator() (boost::shared_ptr<IInteractive> &pObj) const
				{ return pObj->OnGUIEvent(m_nEvent, m_nControlId, m_pControl); }
			
			protected:
				UINT m_nEvent;
				int m_nControlId;
				CDXUTControl *m_pControl;
			};

			struct IOnKeyFunc : public IMemberCaller<IInteractive>
			{
				IOnKeyFunc(UINT nChar, bool bKeyDown, bool bAltDown)
					: m_nChar(nChar), m_bKeyDown(bKeyDown), m_bAltDown(bAltDown) {}
				virtual HRESULT operator() (boost::shared_ptr<IInteractive> &pObj) const
				{ return pObj->OnKey(m_nChar, m_bKeyDown, m_bAltDown); }
			
			protected:
				UINT m_nChar;
				bool m_bKeyDown;
				bool m_bAltDown;
			};

			struct IOnMouseFunc : public IMemberCaller<IInteractive>
			{
				IOnMouseFunc(bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, 
					bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, 
					int xPos, int yPos)
					: m_x(xPos), m_y(yPos), 
					m_bLeftButtonDown(bLeftButtonDown),
					m_bRightButtonDown(bRightButtonDown),
					m_bMiddleButtonDown(bMiddleButtonDown),
					m_bSideButton1Down(bSideButton1Down),
					m_bSideButton2Down(bSideButton2Down),
					m_mouseWheelDelta(nMouseWheelDelta) {}
				
				virtual HRESULT operator() (boost::shared_ptr<IInteractive> &pObj) const
				{	return pObj->OnMouse(m_bLeftButtonDown, m_bRightButtonDown, m_bMiddleButtonDown,
										 m_bSideButton1Down, m_bSideButton2Down,
										 m_mouseWheelDelta, m_x, m_y);
				}
			
			protected:
				int m_x;
				int m_y;
				bool m_bLeftButtonDown, m_bRightButtonDown, m_bMiddleButtonDown;
				bool m_bSideButton1Down, m_bSideButton2Down, m_nMouseWheelDelta;
				int m_mouseWheelDelta;
			};

			////////////////////////////////////
			//GUI Event Dispatcher, Singleton
			template<int TId = 0>
			class CGUIEventManager :
				public _CManagerBaseSingle<CGUIEventManager<TId>, IInteractive, TId>
			{
			public:
				virtual ~CGUIEventManager(void) {}
				static void CALLBACK OnGUIEvent(UINT nEvent, 
					int nControlID, 
					CDXUTControl* pControl, 
					void* pUserContext);
				static void CALLBACK OnKey(UINT nChar, 
					bool bKeyDown, 
					bool bAltDown, 
					void* pUserContext);
				static void CALLBACK OnMouse(bool bLeftButtonDown, 
					bool bRightButtonDown, 
					bool bMiddleButtonDown, 
					bool bSideButton1Down, 
					bool bSideButton2Down, 
					int nMouseWheelDelta, 
					int xPos, int yPos, 
					void* pUserContext);

			private:
				friend class _CManagerBaseSingle<CGUIEventManager<TId>, IInteractive, TId>;
				CGUIEventManager(void) {}
			};

			template<int TId>
			void CALLBACK CGUIEventManager<TId>::OnGUIEvent(UINT nEvent, 
				int nControlID, 
				CDXUTControl* pControl, 
				void* pUserContext)
			{
				IGUIFunc guifunc(nEvent, nControlID, pControl);
				Instance()->Traverse(guifunc); //call OnGUIEvent for all interactive objects
			}

			template<int TId>
			void CALLBACK CGUIEventManager<TId>::OnKey(UINT nChar, 
					bool bKeyDown, 
					bool bAltDown, 
					void* pUserContext)
			{
				IOnKeyFunc keyboardFunc(nChar, bKeyDown, bAltDown);
				Instance()->Traverse(keyboardFunc);
			}

			template<int TId>
			void CALLBACK CGUIEventManager<TId>::OnMouse(bool bLeftButtonDown, 
					bool bRightButtonDown, 
					bool bMiddleButtonDown, 
					bool bSideButton1Down, 
					bool bSideButton2Down, 
					int nMouseWheelDelta, 
					int xPos, int yPos, 
					void* pUserContext)
			{
				IOnMouseFunc mouseFunc(bLeftButtonDown, bRightButtonDown, bMiddleButtonDown,
									   bSideButton1Down, bSideButton2Down,
									   nMouseWheelDelta, xPos, yPos);
				Instance()->Traverse(mouseFunc);
			}


		} //namespace core
	} // namespace engine
} //namespace grflib